﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ExcelExportLib
{
    public class ExcelDocument
    {
        /// <summary>Generated xml file encoding. Usefull if you want to read this file in viewers which didn't suuport unicode</summary>
        public Encoding xml_encoding;

        #region document information
        /// <summary>Document Author</summary>
        public string Author = "ExcelExportLib";

        public string LastAuthor = "ExcelExportLib";

        /// <summary>Document description. You can use multiline strings here</summary>
        public string Description="";

        /// <summary>Document keywords (tags) </summary>
        public string Keywords = "";

        /// <summary>Document subject</summary>
        public string Subject = "";

        /// <summary>Document Title</summary>
        public string Title = "";

        /// <summary>Date when document was created</summary>
        public DateTime CreationDate = DateTime.Now;

        /// <summary>Document Author company</summary>
        public string Company = "ExcelExportLib";

        public string Version = "11.9999"; //didn't know is it usefull or not
        #endregion

        public string DocumentFont = "Arial";

        /// <summary> List of tables </summary>
        public List<ExcelTable> tables = new List<ExcelTable>();

        /// <summary>Number of active table in document. Start number is 0.</summary>
        public int ActiveTable = 0;

        /// <summary>Check for unused styles before saving spreadsheet</summary>
        /// <remarks>Don't use if you use inherited styles in spreadsheet</remarks>
        public bool CheckStylesBeforeSaving = false;

        public ExcelDocument() { }

        /// <summary>Creating new table and adding it to list of tables </summary>
        /// <param name="name">name of new table</param>
        public ExcelTable AddTable(string name)
        {
            if (name.Length == 0)
                throw new ArgumentException("Table name can't be empty!");

            foreach (ExcelTable t in tables)
            {
                if (t.tableName == name)
                    throw new ArgumentException("Table with this name already exists! MS Excel didn't allow duplicate tables names.");
            }

            ExcelTable table = new ExcelTable(name);
            tables.Add(table);
            return table;
        }

        public void Save(string fn)
        {
        
            Encoding enc;
            if (this.xml_encoding != null)
                enc = xml_encoding;
            else
                enc = Encoding.GetEncoding(System.Text.Encoding.Default.CodePage);


            using (XmlTextWriter writer = new XmlTextWriter(fn, enc))
            {
                writer.Formatting = Formatting.Indented;
                writer.WriteStartDocument();
                writer.WriteProcessingInstruction("mso-application", "progid=\"Excel.Sheet\"");

                writer.WriteStartElement("Workbook");
                writer.WriteAttributeString("xmlns", null, null, "urn:schemas-microsoft-com:office:spreadsheet");
                writer.WriteAttributeString("xmlns", "o", null, "urn:schemas-microsoft-com:office:office");
                writer.WriteAttributeString("xmlns", "x", null, "urn:schemas-microsoft-com:office:excel");
                writer.WriteAttributeString("xmlns", "ss", null, "urn:schemas-microsoft-com:office:spreadsheet");
                writer.WriteAttributeString("xmlns", "html", null, "http://www.w3.org/TR/REC-html40");

                WriteDocumentProperties(writer);

                writer.WriteStartElement("ExcelWorkbook");
                writer.WriteAttributeString("xmlns", null, null, "urn:schemas-microsoft-com:office:excel");
                writer.WriteElementString("ActiveSheet", ActiveTable.ToString());
                writer.WriteEndElement();


                writer.WriteStartElement("Styles");
                {
                    //Clean unused CellStyles if flag CheckStylesBeforeSaving is set to true
                    if (CheckStylesBeforeSaving == true)
                    {
                        ExcelCellStyle.CleanUpStyles(this);
                    }
                    //it seems there is no way to serialise without xmlns="" attribute so doing it manually
                    //serializer = new XmlSerializer(typeof (ExcelCellStyle));
                    foreach (ExcelCellStyle ecs in ExcelCellStyle.styles.Values)
                    {
                        writer.WriteStartElement("Style");
                        ecs.WriteXml(writer);
                        writer.WriteEndElement();
                        //serializer.Serialize(writer, ecs);
                    }

                }
                writer.WriteEndElement();

                //collecting cells names for saving
                Dictionary<ExcelCellPosition, string> cells_names = new Dictionary<ExcelCellPosition, string>();
                foreach (ExcelTable table in tables)
                {
                    foreach(ExcelRow row in table.rows)
                        foreach(ExcelCell cel in row.cells)
                        {
                            if (!String.IsNullOrEmpty(cel.cellName))
                                cells_names[cel.GetPosition()] = cel.cellName;
                        }
                }
                //saving names
                if (cells_names.Count != 0)
                {
                    writer.WriteStartElement("Names");
                    foreach (KeyValuePair<ExcelCellPosition, string> kv in cells_names)
                    {
                        writer.WriteStartElement("NamedRange");
                        writer.WriteAttributeString("ss", "Name", null, kv.Value);
                        writer.WriteAttributeString("ss", "RefersTo", null, "=" + kv.Key.ToString());
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }

                //saving tables
                foreach (ExcelTable table in tables)
                {
                    writer.WriteStartElement("Worksheet");
                    table.WriteXml(writer);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }

        private void WriteDocumentProperties(XmlTextWriter writer)
        {
            writer.WriteStartElement("DocumentProperties");
            writer.WriteAttributeString("xmlns", null, null, "urn:schemas-microsoft-com:office:office");
            writer.WriteElementString("Title", Title);
            writer.WriteElementString("Subject", Subject);
            writer.WriteElementString("Author", Author);
            writer.WriteElementString("Keywords", Keywords);

            writer.WriteStartElement("Description");
            writer.WriteRaw(Description.Replace(Environment.NewLine, "&#10;"));
            writer.WriteEndElement();

            writer.WriteElementString("LastAuthor", LastAuthor);
            writer.WriteElementString("Created", CreationDate.GetExcelDate());
            writer.WriteElementString("Company", Company);
            writer.WriteElementString("Version", Version);
            writer.WriteEndElement();
        }
    }
}
